#include "main.h"
#include "spi.h"

const GPIO_Init_Param_Typedef GPIO_SPI1[6] = {
    /*  对于演示板,如果要使用SPI,要将PA4的信号引向PA6,所以要将, 所以要将PA4设置成浮空不用,
        这样字库把信号发送到PA4上,但PA4用跳线连到PA6,让DMA读取*/
    {GPIOA, {LL_GPIO_PIN_3, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 1},                    //  SPI1_CS
    {GPIOA, {LL_GPIO_PIN_5, LL_GPIO_MODE_ALTERNATE, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, LL_GPIO_PULL_DOWN}, 0}, // SPI1_CLK
    {GPIOA, {LL_GPIO_PIN_6, LL_GPIO_MODE_ALTERNATE, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, LL_GPIO_PULL_DOWN}, 0}, // SPI1_MISO
    {GPIOA, {LL_GPIO_PIN_7, LL_GPIO_MODE_ALTERNATE, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, LL_GPIO_PULL_DOWN}, 0}, // SPI1_MOSI
    {0, {0, 0, 0, 0, 0}, 0}};

/* 只写不读的spi函数,在字库芯片上基本用不到 */
void spi1_dma_write(uint8_t *write_buf, uint8_t len)
{
    /* 要等片选释放才表示可以进行下一次传输 */
    while (SPI1_CS1 == 0)
    {
    }
    SPI1_CS1 = 0;
    LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_3,                                   // DMAx and channel
                           (uint32_t)write_buf,                                      // src:  memory
                           LL_SPI_DMA_GetRegAddr(SPI1),                              // dest: spi1
                           LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3)); // direction
    LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, len);
    LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_3);
    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3);
}

/* DMA SPI 不能只读,只读模式clk时钟不会停,不好操作.全双工模式写就是读,不过这样会浪费一部分内存 */
void spi1_dma_read_along_write(uint8_t *write_buf, uint8_t *read_buf, uint8_t len)
{
    /* 要等片选释放才表示可以进行下一次传输 */
    while (SPI1_CS1 == 0)
    {
    }
    SPI1_CS1 = 0;
    /* 先设置读缓冲区 */
    LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_2,                                   // DMAx and channel
                           LL_SPI_DMA_GetRegAddr(SPI1),                              // src:  spi1
                           (uint32_t)read_buf,                                       // dest: memory
                           LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2)); // direction
    LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, len);
    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
    /* 再设置写的数据,要自己规划下这些数据,字库芯片首先要写命令地址,就要命令和地址放在前面 */
    LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_3,                                   // DMAx and channel
                           (uint32_t)write_buf,                                      // src:  memory
                           LL_SPI_DMA_GetRegAddr(SPI1),                              // dest: spi1
                           LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3)); // direction
    LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, len);
    /* 打开写spi通道就会开始发送时钟给从机,打开读是不会发送时钟的 */
    LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_3);
    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3);
}

/* DMA1 spi写中断 */
void DMA1_Channel3_IRQHandler(void)
{
    LL_DMA_ClearFlag_TC3(DMA1);
    while (LL_SPI_IsActiveFlag_BSY(SPI1))
    {
    }
    /* 要等传输完才能关闭通道,释放片选 */
    LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_3);
    LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_3);
    LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
    SPI1_CS1 = 1;
}

/* 一个个字节向写spi */
void spi1_write(uint8_t *write_buf, uint8_t len)
{
    while (len--)
    {
        spi1_byte_write(*write_buf);
        write_buf++;
    }
}

/* 一个个字节的读spi */
void spi1_read(uint8_t *read_buf, uint8_t len)
{
    while (len--)
    {
        *read_buf++ = spi1_byte_read();
    }
}

/* spi dma 初始化 */
void spi1_dma_init()
{
    /* 开启时钟 */
    LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);

    /* DMA 接收中断 */
    NVIC_SetPriority(DMA1_Channel2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
    // NVIC_EnableIRQ(DMA1_Channel2_IRQn);
    /* DMA 发送中断 */
    NVIC_SetPriority(DMA1_Channel3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
    NVIC_EnableIRQ(DMA1_Channel3_IRQn);

    /* 初始化GPIO*/
    GPIO_Init((GPIO_Init_Param_Typedef *)GPIO_SPI1);

    /* SPI1_TX Init */
    LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
    LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_LOW);
    LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_NORMAL);
    LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);
    LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);
    LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE);
    LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_BYTE);
    LL_DMA_ClearFlag_TC3(DMA1);
    LL_SPI_EnableDMAReq_TX(SPI1);
    // LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_3);
    // LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3);

    /* SPI1_RX Init */
    LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
    LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PRIORITY_LOW);
    LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MODE_NORMAL);
    LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PERIPH_NOINCREMENT);
    LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MEMORY_INCREMENT);
    LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PDATAALIGN_BYTE);
    LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MDATAALIGN_BYTE);
    LL_SPI_EnableDMAReq_RX(SPI1);
    LL_DMA_ClearFlag_TC2(DMA1);
    // LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2);
    // LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);

    /* SPI1 parameter configuration*/
    LL_SPI_InitTypeDef SPI_InitStruct = {0};
    SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
    SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
    SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
    SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
    SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
    SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
    SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2;
    SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
    SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
    SPI_InitStruct.CRCPoly = 7;
    LL_SPI_Init(SPI1, &SPI_InitStruct);
    LL_SPI_Enable(SPI1);
}